package com.atguigu.gulimall.basic.swagger2.config;

import cn.hutool.core.util.StrUtil;
import com.atguigu.gulimall.basic.swagger2.properties.GlobalParamsConfig;
import com.atguigu.gulimall.basic.swagger2.properties.SwaggerInnerProperties;
import com.atguigu.gulimall.basic.swagger2.properties.SwaggerProperties;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.ClassUtils;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

import static com.atguigu.gulimall.basic.swagger2.properties.SwaggerProperties.PREFIX;

/**
 * <p>
 * swagger自动配置类
 * 启动条件：
 * 1，配置文件中： ${SwaggerProperties.PREFIX}.enable=true
 * 2，配置文件中不存在： ${SwaggerProperties.PREFIX}.enable 值
 * </p>
 *
 * @author yhd
 * @date 2023-04-27 20:20
 * @description EnableSwagger2 开启swagger2
 * 当且仅当swagger2.enabled=true时，加载List<Docket>的Bean
 */
@RequiredArgsConstructor
@EnableSwagger2WebMvc
@Configuration
@EnableConfigurationProperties(SwaggerProperties.class)
@ConditionalOnProperty(name = "enabled", prefix = PREFIX, havingValue = "true")
public class ServiceAutoConfiguration implements BeanFactoryAware {
    private static final String SEMICOLON = ";";

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final SwaggerProperties properties;

    /**
     * bean 工厂，遍历创建
     */
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Bean
    public List<Docket> createDockets(SwaggerProperties properties) {
        ConfigurableBeanFactory configurableBeanFactory =
                (ConfigurableBeanFactory) beanFactory;
        List<Docket> docketList = new ArrayList<>();
        if (properties.getDockets() != null && properties.getDockets().size() > 0) {
            //遍历创建Docket文档
            if (properties.getDockets().isEmpty()) {
                logger.error("docket配置信息错误");
            } else {
                //遍历创建Docket文档
                for (String group : properties.getDockets().keySet()) {
                    SwaggerInnerProperties innerProperties = properties.dockets.get(group);
                    Docket docket = createOneDocket(innerProperties);
                    //bean工厂创建Bean
                    configurableBeanFactory.registerSingleton(group, docket);
                    docketList.add(docket);
                }
            }


        }
        return docketList;
    }

    private static Predicate<String> ant(final String antPattern) {
        return input -> new AntPathMatcher().match(antPattern, input);
    }


    /**
     * 创建一个Docket
     *
     * @param innerProperties swagger基础信息
     * @return
     */
    public Docket createOneDocket(SwaggerInnerProperties innerProperties) {
        Docket docket = new Docket(DocumentationType.SWAGGER_2);
        docket.groupName(innerProperties.getGroup());
        //配置Api Info
        docket.apiInfo(createApiInfo(innerProperties));

        // base-path处理
        // 当没有配置任何path的时候，解析/**
        if (innerProperties.getBaseUrls().isEmpty()) {
            innerProperties.getBaseUrls().add("/**");
        }
        List<Predicate<String>> basePath = new ArrayList<>();
        for (String path : innerProperties.getBaseUrls()) {
            basePath.add(ant(path));
        }

        // exclude-path处理
        List<Predicate<String>> excludePath = new ArrayList<>();
        for (String path : innerProperties.getExcludePaths()) {
            excludePath.add(ant(path));
        }

        docket.select()
                //设置请求路径
//                .apis(RequestHandlerSelectors.basePackage(innerProperties.getBasePackage()))
                //可以用;分号拼接多个包路径
                .apis(basePackage(innerProperties.getBasePackage()))
                .paths(Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath)))
                .build();
        //配置全局参数
        if (innerProperties.getParamsConfigs().size() > 0) {
            List<Parameter> parameters = setParametes(innerProperties.getParamsConfigs());
            docket.globalOperationParameters(parameters);
        }


        return docket;
    }

    private static com.google.common.base.Predicate<RequestHandler> basePackage(final String basePackage) {
        return input -> declaringClass(input).map(handlerPackage(basePackage)).orElse(true);
    }

    private static Function<Class<?>, Boolean> handlerPackage(final String basePackage) {
        return input -> StrUtil.split(basePackage, SEMICOLON).stream().anyMatch(ClassUtils.getPackageName(input)::startsWith);
    }

    private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
        return Optional.ofNullable(input.declaringClass());
    }


    /**
     * 根据配置信息配置API　INFO信息
     *
     * @param innerProperties
     * @return
     */
    public ApiInfo createApiInfo(SwaggerInnerProperties innerProperties) {
        return new ApiInfoBuilder()
                .version(innerProperties.getVersion())
                .title(innerProperties.getTitle())
                .description(innerProperties.getDescription())
                .contact(new Contact(innerProperties.getContactInfo().getName(), innerProperties.getContactInfo().getUrl(), innerProperties.getContactInfo().getEmail()))
                .build();
    }


    /**
     * 设置全局参数，默认添加到header中
     *
     * @param globaleParams
     * @return
     */
    public List<Parameter> setParametes(List<GlobalParamsConfig> globaleParams) {
        List<Parameter> parameterList = new ArrayList<>();
        for (GlobalParamsConfig config : globaleParams) {
            ParameterBuilder parameterBuilder = new ParameterBuilder();
            // header query cookie形式的
            parameterBuilder.name(config.getName()).description(config.getDescription()).modelRef(new ModelRef(config.getType())).parameterType("header").required(config.isRequired());
            Parameter build = parameterBuilder.build();
            parameterList.add(build);
        }

        return parameterList;
    }

}

